<?php
/*
* Kusht Mud.
* ----
* The MIT License
* Copyright (c) 2010 Andrew Tutt
* ----
*/

/* Basic Server Class
*
* Designed to Abstract the Most Basic Connection Functionality
*/

/* Requires this at some point in the filesystem
* require_once "phpUtilities.inc"; */

/* There is a bug in Chrome currently where refresh does not disconnect it's socket.
   They are fixing it. It does make one concerned about the possibility of nasty disconnected sockets
   staying on the server. It will be fixed at some point. We trust them too much methinks. */

class BasicServer {

	private $master;   /* THE Asynch Listening Socket */
	private $sockets;  /* Array of Connected Sockets  */
	private $connectors;    /* Independently handled connectors */
	private $address;  /* Server Addr */
	private $port;     /* Server Port */
	private $debug;    /* For Debugging */

function __construct($address = "localhost", $port=12345) {
	error_reporting(E_ALL);
	set_time_limit(0);
	ob_implicit_flush();
		
	$this->address = $address;
	$this->port = $port;
		
	$this->master  = $this->WebSocket($address,$port);
	$this->sockets = array($this->master);
	$this->connectors   = array();
	$this->debug   = false;
}

function handle_connections() {
	$master = $this->master;

	$changed = $this->sockets;	
	// Trailing Zero means non-blocking. Trailing NULL would mean block.
	socket_select($changed,$write=NULL,$except=NULL, 0);

	foreach($changed as $socket){
		if($socket==$master){
		  $client=socket_accept($master);
		  if($client<0){ ioHandler::console("socket_accept() failed",$this->debug); continue; }
		  else{ $this->connect($client); }
		}
		else{
		  $bytes = @socket_recv($socket,$buffer,2048,0);
		  if($bytes===false){ 
		    	// Tell the game, via ioHandler, that this connector's gone
  				if ($this->getconnectorbysocket($socket)->loggedin) { 
					ioHandler::unexpectedDisconnect($this->getconnectorbysocket($socket)); 
				}
		  		$this->disconnect($socket); 
			}
		  else{
			$connector = $this->getconnectorbysocket($socket);
			if(!$connector->handshake){ 
				$this->dohandshake($connector,$buffer); 
				ioHandler::onEntry($connector); }
			// Not Logged In? //
			elseif (!$connector->loggedin) { ioHandler::entryProcess($connector,$buffer); }
			// Logged in and GTG //
			else{ ioHandler::process($connector,$buffer); }
		  }
		}
	}
}

function WebSocket($address,$port) {
  $master=socket_create(AF_INET, SOCK_STREAM, SOL_TCP)     or die("socket_create() failed");
  socket_set_option($master, SOL_SOCKET, SO_REUSEADDR, 1)  or die("socket_option() failed");
  socket_bind($master, $address, $port)                    or die("socket_bind() failed");
  socket_listen($master,20)                                or die("socket_listen() failed");
  echo "Server Started : ".date('Y-m-d H:i:s')."\n";
  echo "Master socket  : ".$master."\n";
  echo "Listening on   : ".$address." port ".$port."\n\n";
  return $master;
}

function connect($socket){
  $connector = new BasicConnector();
  $connector->id = uniqid();
  $connector->socket = $socket;
  $connector->server = $this;
  $connector->loggedin = false; // Always Starts False if you want to force login
  array_push($this->connectors,$connector);
  array_push($this->sockets,$socket);
  ioHandler::console($socket." CONNECTED!",$this->debug);
}

function disconnect($socket){
	array_del_elem($this->connectors,$this->getconnectorbysocket($socket));
	array_del_elem($this->sockets,$socket);
	socket_close($socket);
	ioHandler::console($socket." DISCONNECTED!",$this->debug);
}

function dohandshake($connector,$buffer){
  ioHandler::console("\nRequesting handshake...",$this->debug);
  ioHandler::console($buffer,$this->debug);
  list($resource,$host,$origin) = $this->getheaders($buffer);
  ioHandler::console("Handshaking...",$this->debug);
  $upgrade  = "HTTP/1.1 101 Web Socket Protocol Handshake\r\n" .
              "Upgrade: WebSocket\r\n" .
              "Connection: Upgrade\r\n" .
              "WebSocket-Origin: " . $origin . "\r\n" .
              "WebSocket-Location: ws://" . $host . $resource . "\r\n" .
              "\r\n";
  socket_write($connector->socket,$upgrade.chr(0),strlen($upgrade.chr(0)));
  $connector->handshake=true;
  ioHandler::console($upgrade,$this->debug);
  ioHandler::console("Done handshaking...",$this->debug);
  return true;
}

function getheaders($req){
  $r=$h=$o=null;
  if(preg_match("/GET (.*) HTTP/"   ,$req,$match)){ $r=$match[1]; }
  if(preg_match("/Host: (.*)\r\n/"  ,$req,$match)){ $h=$match[1]; }
  if(preg_match("/Origin: (.*)\r\n/",$req,$match)){ $o=$match[1]; }
  return array($r,$h,$o);
}

// Could be made prettier with array_search(), but it works as-is
function getconnectorbysocket($socket){
  $connectors = $this->connectors;
  $found=null;
  foreach($connectors as $connector){
    if($connector->socket==$socket){ $found=$connector; break; }
  }
  return $found;
}

function getconnectors() {
	return $this->connectors;
}

}

?>
